import 'package:equatable/equatable.dart';
import 'package:safety_platform/models/received_notification.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:safety_platform/flutter_persistor.dart';
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'dart:convert';

abstract class PersistTransformer<E> {
  // S decode(String s);

  void decodeTo(String s, E e);
  String encode(E e);
}

abstract class PersistableStat {
  StorageEngine _storageEngine;

  final String persistKey;

  PersistableStat(this.persistKey)
      : _storageEngine = FlutterStorage(
          key: persistKey,
        );

  PersistTransformer<dynamic> get transformer;

  // factory  PersistableStat.formPersist(){
  // transformer.decode(s)
  // }
  // Future<S> formPersist() async {
  //   if (_storageEngine == null) {
  //     _storageEngine = FlutterStorage(
  //       key: persistKey,
  //     );
  //     List
  //   }
  //   Uint8List d = await _storageEngine.load();

  //   print('form persist: ${uint8ListToString(d)}' );
  //   return transformer.decode(uint8ListToString(d));
  // }

  // // static Future<Null> persist() async {

  // // }

  Future<Null> formPersist() async {
    Uint8List str = await _storageEngine.load();

    if (str != null && str.length > 0) {
      transformer.decodeTo(uint8ListToString(str), this);
    }

    print('form persist: ${uint8ListToString(str)} ');
    Future.value(null);
  }

  Future<Null> persist() async {
    String str = transformer.encode(this);

    await _storageEngine.save(stringToUint8List(str));
    Future.value(null);
  }
  // Future<Null> persist() async {
  //   if (_storageEngine == null) {
  //     _storageEngine = FlutterStorage(
  //       key: persistKey,
  //     );
  //   }
  //   String str = transformer.encode(this);
  //   await _storageEngine.save(stringToUint8List(str));
  //   return Future.value(null);
  // }
}

// class B with PersistableState<NotificationState> {
//   String get persistKey => "ABc";
// }

class DataUpdatedStateMixin<S> {
  // DataUpdatedStateMixin(this.totalCount, this.nextPage, this.data)
  //     ;
  int totalCount;

  int nextPage;

  List<S> data;

  @override
  int get hashCode => totalCount.hashCode ^ nextPage.hashCode ^ data.hashCode;


  @override
  bool operator == (Object other) => false;
  // @override


  // bool operator ==(Object other) =>
  //     identical(this, other) ||
  //     other is DataUpdatedStateMixin &&
  //         runtimeType == other.runtimeType &&
  //         totalCount == totalCount &&
  //         nextPage == nextPage &&
  //         data == data;

  bool hasMore() => nextPage != null;

  factory DataUpdatedStateMixin._() {
    return null;
  }

  // @override
  // Type get runtimeType => super.runtimeType;

  @override
  String toString() {
    return 'totalCount $totalCount, next: $nextPage, hasMore: $hasMore()';
  }
}

// class LoadingState {
//     //  LoadingState({loading:false});
//   void markLogind() {
//     loading = true;
//   }

// }
class NotificationState extends PersistableStat
    with DataUpdatedStateMixin<ReceivedNotification> {
  NotificationState(
      {int totalCount, int nextPage, List<ReceivedNotification> data: const []})
      : super('_no_') {
    this.totalCount = totalCount;
    this.nextPage = nextPage;
    this.data = data;
    // _cacheManager =
  }

  //  factory NotificationState.fromPersist() async {
  //    NotificationState state = NotificationState();
  // }

  NotificationState copyWith({
    int totalCount,
    int nextPage,
    List<ReceivedNotification> data,
  }) {
    return NotificationState(
        data: data ?? this.data,
        totalCount: totalCount ?? this.totalCount,
        nextPage: nextPage ?? this.nextPage);
  }

  NotificationStateTransformer get transformer =>
      NotificationStateTransformer();
}

class NotificationStateTransformer
    extends PersistTransformer<NotificationState> {
  void decodeTo(String s, NotificationState stat) {
    if (s != null) {
      dynamic data = json.decode(s);

      List<dynamic> dataList = data['data'] ?? const [];

      stat.data =
          dataList.map((d) => ReceivedNotification.fromJson(d)).toList();
      stat.nextPage = data['nextPage'] ?? 0;
      stat.totalCount = data['totalCount'] ?? 0;
    }
  }

  String encode(NotificationState s) {
    Map<String, dynamic> map = Map();

    map['totalCount'] = s.totalCount;
    map['nextPage'] = s.nextPage;

    List<dynamic> data = s.data.map((n) => n.toJson()).toList();
    map['data'] = data;

    return json.encode(map);
  }
}

class NotificationLoadingState extends NotificationState {}

class NotificationDataUpdated extends NotificationState
    with DataUpdatedStateMixin<ReceivedNotification> {
  // NotificationDataUpdated copyWith({int totolCount, int nextPage, List<ReceivedNotification> data}) {

  // }
}

class NotificationSuccess extends NotificationState {
  // NotificationSuccess(ReceivedNotification data) : super(data);
}

class NotificationReadSuccess extends NotificationState {
  // final ReceivedNotification data;
  // NotificationReadSuccess(this.data);
}

class NotificationDetail extends NotificationState {
  // final ReceivedNotification data;
  // NotificationDetail(this.data);
}

class NotificationDataIsEmpty extends NotificationState {}

class NotificationOperationState extends OperationState<ReceivedNotification> {
  NotificationOperationState(data, {bool submitting: false, Exception error})
      : super(data, submitting: submitting, error: error);

  factory NotificationOperationState.fromData(ReceivedNotification s) {
    return NotificationOperationState(s);
  }

  NotificationOperationState copyWith(
      {ReceivedNotification data, bool submiiting, Exception error}) {
    return NotificationOperationState(data ?? this.data,
        submitting: submiiting ?? this.submitting, error: error ?? this.error);
  }

  @override
  String toString() {
    return '[submit: $submitting]';
  }
}

class SubmittingSuccessState extends NotificationOperationState {
  SubmittingSuccessState(data) : super(data, submitting: false);
}

class OperationState<S> extends Equatable {
  OperationState(this.data, {this.submitting: false, this.error})
      : super([data, submitting, error]);

  bool submitting;

  S data;

  Exception error;

  factory OperationState.fromData(S s) {
    return OperationState(s);
  }
}
